// -*- c-basic-offset: 2 default-tab-width: 2 indent-tabs-mode: t -*-
// vim: autoindent tabstop=2 noexpandtab shiftwidth=2 softtabstop=2
//===------------ Any.h
//
//             This file is part of the reactor project
//                Author: R-Core Team
//
//===---------------------------------------------------------------------===//

#ifndef _ANY_H_
#define _ANY_H_

#include <cstdio>
#include <cstring>

#include "util.h"
#include "RTypes.h"
#include "bc.h"

//#define R_64
namespace R {

	typedef int32_t  r_int;
	typedef uint32_t r_bool;
	typedef double   r_double;
	typedef char*    r_string;

	class RVirtualTable: public VirtualTable {
	public:
		// R type for R object, each object has an identifier for his type.
		word_t type;

		// Virtual R methods.
		struct AnyVTable {
			word_t print;
			word_t toIntOne;
			word_t toLogicalOne;
			word_t subscript;
			word_t subscriptAssign;
			word_t subset;
			word_t subsetAssign;
			word_t newInstance;
			word_t newInstanceWith;
			word_t reverse;
			word_t opposite;
			word_t neg;
			word_t duplicate;
		} vtable;

		// Constructor with type as parameter.
		RVirtualTable(word_t ID, RVirtualTable* vt, bool hasDestructor);

		static uint32_t getBaseSize() {
			return numberOfBaseFunctions() + getNumRInfoValues() + getNumRMethods();
		}

		static uint32_t getNumRInfoValues() {
			return 1;
		}

		static uint32_t getNumRMethods() {
			return sizeof(AnyVTable)/sizeof(word_t);
		}
	};


	/// Defines ID and MASK of 'NAME' class.
	///
#define R_ENUM_TYPE(NAME)													\
	enum { ID = NAME ## _ID, MASK = NAME ## _MASK}

	// Define VT and TYPE of R object 'NAME'.
	//
#define BASE_ABSTRACT_RCLASS(NAME)							\
	public:																				\
	R_ENUM_TYPE(NAME);														\
private:

#define BASE_RCLASS(NAME)												\
	public:																				\
	static RVirtualTable* virtualVT;							\
	R_ENUM_TYPE(NAME);														\
private:

	class Array;

	// All tags for object
	// As long as we wanna be 32bits compatible,
	// the sum must not exceed 16 bits, i.e nothing mus be typed
	// by something greater than uint16_t
#define apply_tags(__todo__, ...)\
  __todo__(HasNA, bool, 1, __VA_ARGS__)

	typedef struct {
#define TAG_STRUCT(__name, __type, __size, ...)\
  __type __name: __size;
	  apply_tags(TAG_STRUCT,)
#undef TAG_STRUCT
	} TagMask;

	// The main tag structure
	union OBJHeader {
	  struct {
		uint16_t gc_bits;
#ifdef R_64
		uint16_t objID;
#endif
		// here we are always align on an half word
		struct {
		  union {
			TagMask tags;
			uint16_t value;
		  } tags;
		} tags; // Aligned on an half-word
	  } headerFields;

	  word_t header;

	  OBJHeader (TagMask m){
		headerFields.tags.tags.tags = m;
	  }
	  OBJHeader (word_t w){
		header = w;
	  }
	};

	/* Any type */
	class Any: public gc {
	private:
		// Method used for type alignment, never called
		//
		virtual void fake_type() {}

		BASE_ABSTRACT_RCLASS(Any)
		
		public:
		Any* attributes;

		Any() { asSelf(Any); self->attributes = NULL; }

		void* operator new(size_t sz, VirtualTable *VT);

		static void markAndTrace(void* source, void* ptr, word_t closure);
		static void markAndTraceRoot(void* ptr, word_t closure);

		/***************************************************/

		// Returns the size of an R VirtualTable
		//
		static size_t getClassVirtualSize() {
			return sizeof(RVirtualTable);
		}

		// getVirtualTable - returns RVirtualTable from instance
		//
		RVirtualTable* getRVirtualTable();

		// getType - returns object's type (type id)
		//
		static word_t getType(Any* self);

		// isOfType<T> - Tests if the given object is of T type.
		//
		template<typename T>
		static bool isOfType(Any* self) {
			R_PARAM(self);
			return !((Any::getType(self) & T::MASK) ^ (T::ID & T::MASK));
		}

		template<typename T>
		static bool isOfType(int selfType) {
			return !((selfType & T::MASK) ^ (T::ID & T::MASK));
		}

		// safeCastTo<T> - Safe cast to T class (with no check).
		//
		template<typename T>
		static T* safeCastTo(Any* self) {
			R_PARAM(self);
			return static_cast<T*>(self);
		}

		// CastTo<T> - Cast to T class with check, raise an error if cast
		// is not correct.
		//
		template<typename T>
		static T* CastTo(Any* self) {
			R_PARAM(self);
			
			rerror_on(!Any::isOfType<T>(self), "Safe cast failed !");
			return static_cast<T*>(self);
		}

		// getOrigVT<T> - Gets original VT (C++) of T Class.
		//
		template<typename T>
		static RVirtualTable* getOrigVT() {
			static T o;
			return o.getRVirtualTable();
		}

		static word_t TType(word_t type1, word_t type2);
		static Any* mkconst(cpool_entry *pe, cpool_entry *cpool_src, Any**cpool_dest);

		// Get attribute of name 'the_attr' from object self.
		//
		Any* getAttr(char* the_attr);

		Any* getAttributes();

		// Set attribute to object self. If attribute exists it is replaced.
		// If not, attribute is added to attributes Vector.
		//
		Any* setAttr(char* the_attr, Any* value);

		// Copy attributes parameter to self attributes.
		//
		void setAttributes(Any* attr);

		/***************************************************
		 *                   Object Tags                   *
		 ***************************************************/

		// Return the tag mask
		TagMask getTags(){
		  asSelf(Any);
		  return ((union OBJHeader)header).headerFields.tags.tags.tags;
		}
		// Change all tags
		void setTags(TagMask val){
		  asSelf(Any);
		  ((OBJHeader)header).headerFields.tags.tags.tags = val;
		}

#define TAG_ACCESSOR(__name, __type, __size, ...)\
  __type get##__name(){\
	asSelf(Any);\
	return self->getTags().__name;\
  }\
  void set##__name(__type val){\
	asSelf(Any);\
	((OBJHeader*)&self->header)->headerFields.tags.tags.tags.__name = val;\
  }

		apply_tags(TAG_ACCESSOR,)
#undef TAG_ACCESSOR

		/***************************************************
		 *                 Virtual Methods                 *
		 ***************************************************/
		// Trace function used for GC Collection. Mark every collectible objects
		// contained in this one.

		virtual void tracer(word_t closure);
		// Print method for R Object.
		virtual void print() { warning("Any::print is an abstract method"); }

		// Converts this object to int Value.
		virtual r_int toIntOne() {
			asSelf(Any);
			warning("Array::toIntOne is Abstract!");
			return 0;
		}

		// Returns logical value for given class objects
		virtual r_bool toLogicalOne() {
			asSelf(Any);
			warning("This object can't be converted to a logical, using FALSE instead");
			return 0;
		}

		virtual Any* duplicate(bool with_attributes = false){
			asSelf(Any);
			return self;
		}
		// subscript of Array
		virtual Any* subscript(Any* idx, bool with_check = true){
			asSelf(Any);
			self->print();
			rfatal("Subscript is only valid for arrays! but found %x", getType(self)); // TODO pretty print of id to names
			return NULL;
		}

		// subscriptAssign of Array
		virtual Any* subscriptAssign(Any *idx, Any *val, Any** out_this){
			asSelf(Any);
			self->print();
			rfatal("Subscript assign is only valid for arrays! but found %x", getType(self));
			return NULL;
		}

		// returns subset of Array
		virtual Any* subset(Any *set) {
			asSelf(Any);
			self->print();
			rfatal("Subset is only valid for arrays! ");
			return NULL;
		}

		virtual Any* subsetAssign(Any *set, Any *vals) {
			asSelf(Any);
			self->print();
			rfatal("Subset assign is only valid for arrays! ");
			return NULL;
		}

		virtual Any* reverse(){
			asSelf(Any);
			rfatal("Reverse is only valid for arrays!");
			return 0;
		}

		virtual Any* opposite(){
			asSelf(Any);
			rfatal("Any::opposite is only valid for numbers!");
			return 0;
		}

		virtual Any* neg(){
			asSelf(Any);
			rfatal("Any::neg is only valid for numbers!");
			return 0;
		}

		static void printNull(); // FIXME remove me when NULL will be a proper type
	};
}

#endif
